import os
import re

import Class
import Registry
import Module


# noinspection PyMethodMayBeStatic
class RawPythonNamer(Module.PythonNamer):
    def package(self):
        return "Raw"

    def _to_python(self, name):
        if '<' in name:
            # Remove STL containers' allocator template argument.
            for container in ("vector", "list", "set", "deque",):
                if name.startswith(container):
                    name = name[:name.index(',')] + '>'
                    break

            name = self.normalize_template(name)

        return name


# noinspection PyMethodMayBeStatic
class RawHeaderProvider(Module.HeaderProvider):
    def klass(self, cls):
        if cls == "Y":
            return "Y.hpp",

        return ()

    def module(self, name):
        return "X.hpp", "Y.hpp",

    def normalize(self, full_path):
        return os.path.split(full_path)[1]

    def pch(self):
        return '#include "StdAfx.hpp"'


class RawFlagsAssigner(Module.FlagsAssigner):

    _managed_by_cxx = (
        "CXXHeld",
        "CXXHeld2",
    )

    def assign(self, cls_name):
        cls = Registry.get_class(cls_name)

        for nonowned in self._managed_by_cxx:
            if cls.full_name == nonowned or cls.is_derived_from(nonowned):
                return "pbpp::LifeTime::CXX"

        return "pbpp::LifeTime::PYTHON"


# noinspection PyMethodMayBeStatic
class RawBlacklist(Module.Blacklist):

    _namespaces = {
        "std", "stdext", "__gnu_cxx",
        "__castxml",
    }

    _class_patterns = (
        re.compile(r"\w*_class_type_info_pseudo\w*"),

        # STL stuffs
        re.compile(r"(\w+::)*(const_)?iterator"),
        re.compile(r"std::allocator"),
        re.compile(r"std::_Vector_base"),
    )

    _classes = {
        "PyObject",
        "_PySelf",
        "Unknown",
    }

    _base_patterns = (
        re.compile(r"std::_Vector_base"),
    )

    _bases = {
        "_PySelf",
    }

    _method_patterns = (
        re.compile(r".+\W((r)?begin|(r)?end)"),
        re.compile(r".+\W_M_\w+"),
    )

    _methods = ()

    _free_function_patterns = ()
    _free_functions = ()

    _global_constants_patterns = ()

    _global_constants = {
        "MyVar",
    }

    _fields = ()

    def __init__(self):
        Module.Blacklist.__init__(self)

        # for cls in ("Dummy",):
        #     self.add_simple_dummy_class(cls)

    def namespace(self, ns):
        return ns in self._namespaces

    def klass(self, cls):
        for pattern in self._class_patterns:
            if pattern.match(cls):
                return True

        return cls in self._classes

    def base(self, full_name):
        for pattern in self._base_patterns:
            if pattern.match(full_name):
                return True

        return full_name in self._bases

    def method(self, mname):
        for pattern in self._method_patterns:
            if pattern.match(mname):
                return True

        return mname in self._methods

    def free_function(self, name):
        for pattern in self._free_function_patterns:
            if pattern.match(name):
                return True

        return name in self._free_functions

    def global_constants(self, full_decl_map):
        full_decl = "%s %s" % (full_decl_map["TYPE"], full_decl_map["FULL_NAME"])
        for pattern in self._global_constants_patterns:
            if pattern.match(full_decl):
                return True

        return full_decl_map["FULL_NAME"] in self._global_constants

    def field(self, cls, f):
        if f.startswith("m_") or f.startswith("sm_") or f.startswith("ms_"):
            return True
        else:
            return False


# noinspection PyMethodMayBeStatic
class ProcessingDoneListener:
    def __init__(self):
        pass

    def on_processing_done(self, module):
        X_Injected = module.free_functions.methods.pop("X_Injected", None)
        if X_Injected:
            X = Registry.get_class("X")
            X.inject_as_method(X_Injected, "Injected", None)

        X_Injected2 = module.free_functions.methods.pop("X_Injected2", None)
        if X_Injected2:
            X = Registry.get_class("X")
            X.inject_as_method(X_Injected2, "Injected2", None)
